
package at.fhj.ase.crypto.common.util;

import java.util.StringTokenizer;
import java.util.Vector;

/**
 * Class representing the network protocol of this application.
 * <p>
 * The usage of it is trivial, just create an instance like this:
 * 
 * <pre>
 * new CommandBuilder(CommandBuilder.AUTHENTICATED).addCommand(&quot;Authentication successful&quot;);
 * </pre>
 * 
 * The example above instantiates a message of type {@link #AUTHENTICATED} and also adds the text (data) with the following
 * {@link CommandBuilder#addCommand(String)}.<br>
 * The latter method can be used like a builder pattern, therefore also this class is called "CommandBuilder".<br>
 * To get the result out of the instance, just call {@link #toString()} to fully assemble the message as a {@link String}.<br>
 * This String should then be ready to send over the network.
 * <p>
 * We can therefore say: The constants like {@link #PUBLIC_KEY}, {@link #AUTHENTICATED}, {@link #DATA} etc. are the keys of our protocol that
 * <strong>identify</strong> what we intend to send over the network or which message is received.<br>
 * Therefore this class can (and should!) be used by clients as well as servers, both receiving and sending data and vice versa.
 * <p>
 * There are convenience implementations like the {@link #parse(String)} and the {@link #parseAndReturnValue(String, String, int)} methods.<br>
 * The first one returns the whole retrieved/send command as a {@link Vector}, separated by {@link #SEPARATOR},<br>
 * the second one also returns a particular item of the {@link Vector} with an expected key.
 * <p>
 * Note that internally a {@link StringBuilder} is used to assemble the message. This also has performance reasons.
 * 
 * @author sfe | www.fh-joanneum.at | Practical Aspects of Cryptography
 */
public class CommandBuilder {
  /** The platform independent new line, or also called "line feet" */
  public static final String NEWLINE = System.getProperty("line.separator", "\n");
  /** The separator that is used to distinguish between valid messages and commands */
  public static final char SEPARATOR = ';';
  /** The space that is used as divider between words or in a sentence */
  public static final char SPACE = ' ';

  /** The key that indicates a public key transmission (may be used in the beginning of a message) */
  public static final String PUBLIC_KEY = "PUBLIC_KEY";
  /** The key that indicates a session key transmission (may be used in the beginning of a message) */
  public static final String SESSION_KEY = "SESSION_KEY";
  /** The key that indicates a signature transmission (may be used in the beginning of a message) */
  public static final String SIGNATURE = "SIGNATURE";
  /** The key that indicates that the server authenticated the client successfully (may be used in the beginning of a message) */
  public static final String AUTHENTICATED = "AUTHENTICATED";
  /** The key that indicates that the server rejected the client (may be used in the beginning of a message) */
  public static final String DENIED = "DENIED";
  /** The key that indicates that normal data traffic from server to client (may be used in the beginning of a message) */
  public static final String DATA = "DATA";

  /** The final {@link StringBuilder} instance this class is working on/with */
  private final StringBuilder stringBuilder;

  /**
   * Convenience method creating a {@link Vector} of the provided <code>input</code> {@link String}.<br>
   * The input message is parsed for {@link #SEPARATOR} occurrences and creates the vector elements using a {@link StringTokenizer}.<br>
   * Therefore each token can be realized by leading and ending {@link #SEPARATOR}.
   * 
   * @param input the {@link String} that shall be parsed and created to a vector (must contain {@link #SEPARATOR} for proper results)
   * @return a {@link Vector} instance that contains the input string dividig up into elements by token {@link #SEPARATOR}
   */
  public static Vector<String> parse(final String input) {
    final Vector<String> result = new Vector<String>();
    final StringTokenizer tokenizer = new StringTokenizer(input, String.valueOf(SEPARATOR));

    while (tokenizer.hasMoreTokens()) {
      result.add(tokenizer.nextToken());
    }

    return result;
  }

  /**
   * Parses a message for the expected start {@link String} (which could be e.g. {@link CommandBuilder#DATA}) and at the given position in the {@link Vector}.<br>
   * The {@link Vector} is resulted out of {@link CommandBuilder#parse(String)} which is used internally.<br>
   * 
   * @param msg the message that shall be parsed
   * @param expectedStart the expected start {@link String} which identifier the message
   * @param valuePosition
   * @return the parsed string that is followed by the indicating string
   * @throws IllegalArgumentException if the expected and the current start message are not equal (e.g. we have a {@link CommandBuilder#DATA} message, but it is
   * a {@link CommandBuilder#PUBLIC_KEY})
   */
  @SuppressWarnings("unchecked")
  public static String parseAndReturnValue(final String msg, final String expectedStart, final int valuePosition) {
    final Vector<String> parsedMessage = (Vector<String>) CollectionUtil.checkEmptyOrNull(CommandBuilder.parse(msg));
    if (!parsedMessage.get(0).equalsIgnoreCase(expectedStart)) {
      throw new IllegalArgumentException("Message does not contain expected start key " + expectedStart);
    }

    return parsedMessage.get(valuePosition);
  }

  /**
   * Constructor creating a new {@link CommandBuilder} instance with the first and initial command as {@link String} parameter.
   * 
   * @param firstCommand the first and initial command this instance shall be initiated with
   */
  public CommandBuilder(final String firstCommand) {
    stringBuilder = new StringBuilder(firstCommand);
  }

  /**
   * Adds the provided String <code>command</code> to the command string with a leading {@link #SEPARATOR}.
   * 
   * @param command the command that shall be added to this {@link CommandBuilder}
   * @return the {@link CommandBuilder} instance again to enable the developer using it like a builder pattern, e.g.
   * 
   * <pre>
   * CommandBuilde.addCommand(String).addCommand(String)
   * </pre>
   */
  public CommandBuilder addCommand(final String command) {
    stringBuilder.append(SEPARATOR);
    stringBuilder.append(command);
    return this;
  }

  /**
   * @return the assembled and created command as {@link String} representation.
   */
  @Override
  public String toString() {
    return stringBuilder.toString();
  }
}
